今天就來做收支記錄界面的收尾,預計會把收、支記錄以顏色區分,並透過日期分組,最後再加上一點統計資料。
在「快速記帳」中的每個記錄的背景都是黑色的,而在收支記錄中,預計會用綠色顯示收入,紅色顯示支出,如下:
if transaction.type == .INCOME {
    amountLabel.textColor = MMColor.black
    amountLabel.backgroundColor = MMColor.green
    playButton.setTitleColor(MMColor.black, for: .normal)
    playButton.backgroundColor = MMColor.green
} else {
    amountLabel.textColor = MMColor.white
    amountLabel.backgroundColor = MMColor.red
    playButton.setTitleColor(MMColor.white, for: .normal)
    playButton.backgroundColor = MMColor.red
}
此時我們會面臨一個問題,就是界面怎麽跑都是紅色的,原因是因為我們「偽裝」的 TransactinoType,沒有加入 == 的功能,系統不知道該怎麼比較兩者是否「相等」,我們可以再來幫 TransactionType 補上:
extension TransactionType {
    static func ==(lhs: TransactionType, rhs: TransactionType) -> Bool {
        return lhs.rawValue == rhs.rawValue
    }
}
如此一來,就能把系統騙得不要不要的。
除了讓每筆資料都能有漂亮的綠色、紅色,我還想以日期區分記錄,並顯示每天總共收、支多少錢。
在 Core Data 中每筆收支記錄都有日期,但我們不能直接用這個日期欄位來做分組,因為該欄位還同時包含時間,而我們只需要「年、月、日」,因此我們可以在 Transaction 中新增一個屬性,把日期欄位轉換成我們需要的格式,再請 NSFetchedResultsController 使用我們指定的屬性分組,如下:
@objc var createdAtDay: String {
    get {
        let formatter = DateFormatter()
        formatter.dateStyle = .medium
        formatter.timeStyle = .none
        formatter.timeZone = TimeZone.current
        return formatter.string(from: createdAt)
    }
}
let fetchedResultsController = NSFetchedResultsController<Transaction>(fetchRequest: request, managedObjectContext: viewContext, sectionNameKeyPath: #keyPath(Transaction.createdAtDay), cacheName: nil)
首先我們要先建立一個客製化的 UITableViewHeaderFooterView,如下:
class TransactionTableHeaderView: UITableViewHeaderFooterView {
}
接著再設定 UITableView 使用這個客製化的 Header,如下:
register(TransactionTableHeaderView.self, forHeaderFooterViewReuseIdentifier: NSStringFromClass(TransactionTableHeaderView.self))
接著我們就可以新增一些我們需要的資料,如下:
class TransactionTableHeaderView: UITableViewHeaderFooterView {
    var totalIncome = NSDecimalNumber.zero {
        didSet {
            totalIncomeLabel.text = "收入 \(totalIncome)"
        }
    }
    var totalExpense = NSDecimalNumber.zero {
        didSet {
            totalExpenseLabel.text = "支出 \(totalExpense)"
        }
    }
    var date = Date() {
        didSet {
            let calendar = Calendar.current
            let components = calendar.dateComponents([.year, .month, .day], from: date)
            if let day = components.day {
                dayLabel.text = String(describing: day)
                dayLabel.sizeToFit()
            }
            if let month = components.month, let year = components.year {
                monthYearLabel.text = "/ \(month) / \(year)"
                monthYearLabel.sizeToFit()
            }
        }
    }
}
最後再補上 UITableView 相關的 Delegate 就能完成囉:
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
    guard let sectionInfo = fetchedResultsController.sections?[section] else {
        return nil
    }
    let headerView = (tableView as! TransactionTableView).dequeueReusableHeaderView()
    var totalIncome = NSDecimalNumber.zero
    var totalExpense = NSDecimalNumber.zero
    sectionInfo.objects?.forEach { transaction in
        guard let transaction = transaction as? Transaction else {
            return
        }
        guard transaction.amount != NSDecimalNumber.notANumber else {
            return
        }
        if transaction.type == .INCOME {
            totalIncome = totalIncome.adding(transaction.amount)
        } else {
            totalExpense = totalExpense.adding(transaction.amount)
        }
    }
    let formatter = DateFormatter()
    formatter.dateStyle = .medium
    formatter.timeStyle = .none
    formatter.timeZone = TimeZone.current
    headerView.date = formatter.date(from: sectionInfo.name) ?? Date()
    headerView.totalIncome = totalIncome
    headerView.totalExpense = totalExpense
    return headerView
}

你以為是 GIF 嗎?抱歉,我懶。
程式碼:GitHub
程式碼隨著時間的推移,又越來越髒了,果然沒有了同事這種生物的時候,人就會開始墮落。
你以為下一篇又要整理程式碼嗎?哦不,太天真了,功能沒有出來之前,老闆是不會讓你休息的。
還有很多功能要生出來,下一篇預計會開始加入統計、圖表相關功能。
備註:其實後來發現快速記帳、收支記錄應該合在一起,使用者在新增時預設為快速記帳,但是多給一個按鈕讓使用者展開填更多。